package com.vito.framework.base.interceptor;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.vito.framework.cache.CacheKeys;
import com.vito.framework.rest.bean.AppSession;
import com.vito.util.CookieUtil;
import com.vito.util.IpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;

import javax.servlet.FilterConfig;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Map;

/**
 * 记录log
 *
 * @author : zhaowentao
 * @version : 2019年6月13日
 * @date : 2019年6月13日 下午4:53:34
 */
@Component
public class AppLogFilter implements Filter {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    //记录日志长度
    private int maxLogLength = 2048;
    private String logFormat = "token：%s|#method：%s|#ip：%s|#cost：%s|#request_url：%s|#header：%s|#session_data：%s|#request_data：%s|#response_data：%s";

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        //上传文件请求不能记录日志
        String contentType = req.getContentType();
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        if (request.getMethod().contains("POST")) {
            if (contentType != null && contentType.contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {
                chain.doFilter(req, res);
                return;
            }
        }

        String headerdata = null;
        String sessiondata = null;
        String parameter = null;
        String reqdata = null;
        String resdata = null;
        String errormsg = null;
        String apptoken = null;
        long start = System.currentTimeMillis();

        RequestWrapper requestWrapper = new RequestWrapper(request);
        ResponseWrapper responseWrapper = new ResponseWrapper(response); // 包装响应对象 response 并缓存响应数据
        try {

            chain.doFilter(requestWrapper, responseWrapper);
            byte[] resbyte = responseWrapper.getBytes();
            response.getOutputStream().write(resbyte);//最后注意需要请reponsewrapper的内容写入到原始response

            //获取header数据
            apptoken = request.getHeader(CacheKeys.APP_TOKEN);
            if (StrUtil.isEmpty(apptoken)) {
                apptoken = CookieUtil.getCookieValue(request, CacheKeys.USER_TOKEN);
            }
            if (apptoken != null) {
                AppSession appSession = (AppSession) request.getSession().getAttribute(CacheKeys.APP_TOKEN + apptoken);
                sessiondata = appSession.toString();
            }
            //获取请求参数
            Map<String, String[]> map = request.getParameterMap();
            if (map != null && map.size() > 0) {
                parameter = JSON.toJSONString(map);
            }

            InputStream in = requestWrapper.getInputStream();
            //获取request数据
            if (checkContentType(request.getContentType())) {
                reqdata = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
            }
            //获取response数据
            if (resbyte != null && checkContentType(response.getContentType())) {
                resdata = new String(resbyte);
            }
            if (reqdata != null && reqdata.getBytes().length > maxLogLength) {
                reqdata = "数据过大";
            }
            if (resdata != null && resdata.getBytes().length > (maxLogLength / 2)) {
                resdata = "数据过大";
            }
        } catch (Exception e) {
            errormsg = e.getMessage() + "\n" + Arrays.toString(e.getStackTrace());
        }
        long end = System.currentTimeMillis();

        long cost = end - start;
        saveLog(request, cost, sessiondata, headerdata, parameter, reqdata, resdata, errormsg);
    }

    @Override
    public void destroy() {
        // Do nothing
    }

    private void saveLog(HttpServletRequest request, long cost, String sessiondata, String headerdata, String parameter,
                         String reqdata, String resdata, String errormsg) {
        try {
            String method = request.getMethod();
            String requrl = request.getRequestURL().toString();
            String token = request.getHeader(CacheKeys.APP_TOKEN);
            String ip = IpUtil.getIpAddr(request);
            String costMsg = "";
            if (cost < 1000) {
                costMsg = "|#耗时小于1秒";
            } else if (cost >= 1000 && cost < 3000) {
                costMsg = "|#耗时1-3秒";
            } else if (cost >= 3000 && cost < 6000) {
                costMsg = "|#耗时3-6秒";
            } else if (cost >= 6000 && cost < 10000) {
                costMsg = "|#耗时6-10秒";
            } else if (cost >= 10000) {
                costMsg = "|#耗时大于10秒";
            }
            logger.info(String.format(logFormat, token, method, ip, cost + costMsg, requrl, headerdata, sessiondata, reqdata, resdata));


        } catch (Exception e) {
            logger.error(e.getMessage());
        }
    }

    private boolean checkContentType(String contentType) {
        return contentType != null && contentType.toLowerCase().indexOf("application/json") >= 0;
    }
}
